﻿using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Text;

public static class Ext
{
    /// <summary>
    /// 获取最初导致异常的Message
    /// </summary>
    /// <param name="ex">异常对象</param>
    /// <returns></returns>
    public static string RawMsg(this Exception ex)
    {
        while (ex.InnerException != null)
        {
            ex = ex.InnerException;
        }
        return ex.Message;
    }

    /// <summary>
    /// 在给定所有Keys 搜索获取value
    /// </summary>
    public static TValue GetValue<TValue>(this IDictionary<string, string> @this, TValue defaultValue, params string[] keys)
    {
        foreach (var key in keys)
        {
            if (@this.ContainsKey(key))
                return (TValue)Convert.ChangeType(@this[key], typeof(TValue));
        }
        return defaultValue;
    }

    public static string ToJsonString(this object @object)
    {
        //序列化 对象成Json字符串
        Newtonsoft.Json.Formatting format = Newtonsoft.Json.Formatting.None;
        //时间格式化
        IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss:fff" };
        //枚举类型格式化
        StringEnumConverter emConverter = new StringEnumConverter();
        List<Newtonsoft.Json.JsonConverter> lstJCvtr = new List<Newtonsoft.Json.JsonConverter>();
        lstJCvtr.Add(dtConverter);
        lstJCvtr.Add(emConverter);
        return Newtonsoft.Json.JsonConvert.SerializeObject(@object, format, lstJCvtr.ToArray());
    }

    public static T ToObject<T>(this string json)
    {
        //时间格式化
        IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss:fff" };
        //枚举类型格式化
        StringEnumConverter emConverter = new StringEnumConverter();
        List<Newtonsoft.Json.JsonConverter> lstJCvtr = new List<Newtonsoft.Json.JsonConverter>();
        lstJCvtr.Add(dtConverter);
        lstJCvtr.Add(emConverter);
        return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
    }


    /// <summary>
    /// 引用类型对象的序列化（深度克隆）
    /// 注：使用之前先将对象标记为  [Serializable] 可序列化。
    /// </summary>
    /// <typeparam name="T">对象类型</typeparam>
    /// <param name="RealObject"></param>
    /// <returns></returns>
    public static T Clone<T>(this T RealObject)
    {
        using (Stream objectStream = new MemoryStream())
        {
            //利用 System.Runtime.Serialization序列化与反序列化完成引用对象的复制  
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(objectStream, RealObject);
            objectStream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(objectStream);
        }
    }

    /// <summary>
    /// 将DataRow转为 IDictionary 类型数据
    /// </summary>
    /// <param name="this"></param>
    /// <returns></returns>
    public static Dictionary<string, object> ToDictionary(this DataRow @this)
    {
        var dict = new Dictionary<string, object>();
        foreach (DataColumn column in @this.Table.Columns)
        {
            dict.Add(column.ColumnName, @this[column]);
        }
        return dict;
    }


    /// <summary>
    /// 字符串的Md5值
    /// </summary>
    public static string Md5Hash(this string input)
    {
        if (input == null)
        {
            return null;
        }

        MD5 md5Hash = MD5.Create();
        // 将输入字符串转换为字节数组并计算哈希数据
        byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
        // 创建一个 Stringbuilder 来收集字节并创建字符串
        StringBuilder sBuilder = new StringBuilder();
        // 循环遍历哈希数据的每一个字节并格式化为十六进制字符串
        for (int i = 0; i < data.Length; i++)
        {
            sBuilder.Append(data[i].ToString("x2"));
        }
        // 返回十六进制字符串
        return sBuilder.ToString();
    }


    #region NameValueCollection 扩展

    /// <summary>
    /// 返回可写 NameValueCollection集合
    /// </summary>
    public static NameValueCollection NoReadonly(this NameValueCollection @this)
    {
        if (@this == null)
        {
            return new NameValueCollection();
        }
        return new NameValueCollection(@this);
    }

    /// <summary>
    /// 是否包含某个键
    /// </summary>
    /// <param name="keyName">键名</param>
    /// <param name="valueIsSpace">value值 是否 允许 为空字符串</param>
    public static bool ContainsKey(this NameValueCollection @this, string keyName, bool valueIsSpace = false)
    {
        if (@this == null || @this.Count <= 0)
        {
            return false;
        }

        string[] values = @this.GetValues(keyName);

        if (values == null)
        {
            return false;
        }

        if (valueIsSpace)
        {
            return true;
        }

        int ct = 0;
        foreach (var val in values)
        {
            if (string.IsNullOrWhiteSpace(val))
            {
                ct++;
            }
        }
        return !(ct == values.Length);
    }

    /// <summary>
    /// 是否包含某个值
    /// </summary>
    /// <param name="value">值</param>
    /// <param name="ignoreCase">是否忽略大小写</param>
    public static bool ContainsValue(this NameValueCollection @this, string value, bool ignoreCase = true)
    {
        if (@this == null || @this.Count <= 0)
        {
            return false;
        }

        StringComparison strCmp = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.CurrentCulture;
        foreach (var keyName in @this.AllKeys)
        {
            if (@this.Get(keyName).Equals(value, strCmp))
            {
                return true;
            }
        }
        return false;
    }


    /// <summary>
    /// 尝试获取 某个键的值
    /// </summary>
    /// <param name="keyName">键名</param>
    /// <param name="values">values值数组</param>
    public static bool TryGetValues(this NameValueCollection @this, string keyName, out string[] values)
    {
        values = null;
        return TryGetValues(@this, keyName, false, out values);
    }

    /// <summary>
    /// 尝试获取 某个键的值
    /// </summary>
    /// <param name="keyName">键名</param>
    /// <param name="valueIsSpace">value值 是否 允许 为空字符串</param>
    /// <param name="values">values值数组</param>
    public static bool TryGetValues(this NameValueCollection @this, string keyName, bool valueIsSpace, out string[] values)
    {
        values = null;

        if (@this == null || @this.Count <= 0)
        {
            return false;
        }

        values = @this.GetValues(keyName);

        if (values == null)
        {
            return false;
        }

        if (valueIsSpace && values.Length > 0)
        {
            return true;
        }
        else
        {
            int ct = 0;
            foreach (var val in values)
            {
                if (string.IsNullOrWhiteSpace(val))
                {
                    ct++;
                }
            }
            return !(ct == values.Length);
        }
    }



    /// <summary>
    /// 尝试获取 某个键的值
    /// </summary>
    /// <param name="keyName">键名</param>
    /// <param name="values">value值数组</param>
    public static bool TryGetValue(this NameValueCollection @this, string keyName, out string value)
    {
        value = null;
        return TryGetValue(@this, keyName, false, out value);
    }


    /// <summary>
    /// 尝试获取 某个键的值
    /// </summary>
    /// <param name="keyName">键名</param>
    /// <param name="valueIsSpace">value值 是否 允许 为空字符串</param>
    /// <param name="values">value值数组</param>
    public static bool TryGetValue(this NameValueCollection @this, string keyName, bool valueIsSpace, out string value)
    {
        value = null;

        if (@this == null || @this.Count <= 0)
        {
            return false;
        }

        value = @this.Get(keyName);

        if (value == null)
        {
            return false;
        }

        if (valueIsSpace && value.Length > 0)
        {
            return true;
        }
        else
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                return false;
            }
            return true;
        }
    }

    public static bool Remove(this NameValueCollection @this, string keyName, string value)
    {
        if (@this == null || @this.Count <= 0)
        {
            return false;
        }

        var lstVals = @this.GetValues(keyName)?.ToList();

        if (lstVals == null)
        {
            return false;
        }

        int index = lstVals.IndexOfCompare(value);

        if (index <= -1)
        {
            return false;
        }

        lstVals.RemoveAt(lstVals.IndexOfCompare(value));
        @this.Remove(keyName);

        for (int j = 0; j < lstVals.Count; j++)
        {
            @this.Add(keyName, lstVals[j]);
        }
        return true;
    }

    public static TValue GetValue<TValue>(this NameValueCollection @this,  TValue defaultValue, params string[] keys)
    {
        if (@this == null || @this.Count <= 0)
        {
            return defaultValue;
        }

        foreach (var key in keys)
        {
            string value;
            if (@this.TryGetValue(key, out value))
                return (TValue)Convert.ChangeType(value, typeof(TValue));
        }
        return defaultValue;

    }

    private static int IndexOfCompare(this IEnumerable<string> @this, string item, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase)
    {
        if (@this == null || !@this.Any())
        {
            return -1;
        }

        int j = -1;
        foreach (string curStr in @this)
        {
            j++;

            if (
                (curStr == null && item == null) ||
                (curStr != null && curStr.Equals(item, comparisonType))
               )

            {
                return j;
            }
        }
        return -1;
    }


    #endregion
}